home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / lib / python2.6 / robotparser.py < prev    next >
Encoding:
Python Source  |  2010-12-26  |  6.8 KB  |  218 lines

  1. """ robotparser.py
  2.  
  3.     Copyright (C) 2000  Bastian Kleineidam
  4.  
  5.     You can choose between two licenses when using this package:
  6.     1) GNU GPLv2
  7.     2) PSF license for Python 2.2
  8.  
  9.     The robots.txt Exclusion Protocol is implemented as specified in
  10.     http://info.webcrawler.com/mak/projects/robots/norobots-rfc.html
  11. """
  12. import urlparse
  13. import urllib
  14.  
  15. __all__ = ["RobotFileParser"]
  16.  
  17.  
  18. class RobotFileParser:
  19.     """ This class provides a set of methods to read, parse and answer
  20.     questions about a single robots.txt file.
  21.  
  22.     """
  23.  
  24.     def __init__(self, url=''):
  25.         self.entries = []
  26.         self.default_entry = None
  27.         self.disallow_all = False
  28.         self.allow_all = False
  29.         self.set_url(url)
  30.         self.last_checked = 0
  31.  
  32.     def mtime(self):
  33.         """Returns the time the robots.txt file was last fetched.
  34.  
  35.         This is useful for long-running web spiders that need to
  36.         check for new robots.txt files periodically.
  37.  
  38.         """
  39.         return self.last_checked
  40.  
  41.     def modified(self):
  42.         """Sets the time the robots.txt file was last fetched to the
  43.         current time.
  44.  
  45.         """
  46.         import time
  47.         self.last_checked = time.time()
  48.  
  49.     def set_url(self, url):
  50.         """Sets the URL referring to a robots.txt file."""
  51.         self.url = url
  52.         self.host, self.path = urlparse.urlparse(url)[1:3]
  53.  
  54.     def read(self):
  55.         """Reads the robots.txt URL and feeds it to the parser."""
  56.         opener = URLopener()
  57.         f = opener.open(self.url)
  58.         lines = [line.strip() for line in f]
  59.         f.close()
  60.         self.errcode = opener.errcode
  61.         if self.errcode in (401, 403):
  62.             self.disallow_all = True
  63.         elif self.errcode >= 400:
  64.             self.allow_all = True
  65.         elif self.errcode == 200 and lines:
  66.             self.parse(lines)
  67.  
  68.     def _add_entry(self, entry):
  69.         if "*" in entry.useragents:
  70.             # the default entry is considered last
  71.             if self.default_entry is None:
  72.                 # the first default entry wins
  73.                 self.default_entry = entry
  74.         else:
  75.             self.entries.append(entry)
  76.  
  77.     def parse(self, lines):
  78.         """parse the input lines from a robots.txt file.
  79.            We allow that a user-agent: line is not preceded by
  80.            one or more blank lines."""
  81.         # states:
  82.         #   0: start state
  83.         #   1: saw user-agent line
  84.         #   2: saw an allow or disallow line
  85.         state = 0
  86.         linenumber = 0
  87.         entry = Entry()
  88.  
  89.         for line in lines:
  90.             linenumber += 1
  91.             if not line:
  92.                 if state == 1:
  93.                     entry = Entry()
  94.                     state = 0
  95.                 elif state == 2:
  96.                     self._add_entry(entry)
  97.                     entry = Entry()
  98.                     state = 0
  99.             # remove optional comment and strip line
  100.             i = line.find('#')
  101.             if i >= 0:
  102.                 line = line[:i]
  103.             line = line.strip()
  104.             if not line:
  105.                 continue
  106.             line = line.split(':', 1)
  107.             if len(line) == 2:
  108.                 line[0] = line[0].strip().lower()
  109.                 line[1] = urllib.unquote(line[1].strip())
  110.                 if line[0] == "user-agent":
  111.                     if state == 2:
  112.                         self._add_entry(entry)
  113.                         entry = Entry()
  114.                     entry.useragents.append(line[1])
  115.                     state = 1
  116.                 elif line[0] == "disallow":
  117.                     if state != 0:
  118.                         entry.rulelines.append(RuleLine(line[1], False))
  119.                         state = 2
  120.                 elif line[0] == "allow":
  121.                     if state != 0:
  122.                         entry.rulelines.append(RuleLine(line[1], True))
  123.                         state = 2
  124.         if state == 2:
  125.             self._add_entry(entry)
  126.  
  127.  
  128.     def can_fetch(self, useragent, url):
  129.         """using the parsed robots.txt decide if useragent can fetch url"""
  130.         if self.disallow_all:
  131.             return False
  132.         if self.allow_all:
  133.             return True
  134.         # search for given user agent matches
  135.         # the first match counts
  136.         url = urllib.quote(urlparse.urlparse(urllib.unquote(url))[2]) or "/"
  137.         for entry in self.entries:
  138.             if entry.applies_to(useragent):
  139.                 return entry.allowance(url)
  140.         # try the default entry last
  141.         if self.default_entry:
  142.             return self.default_entry.allowance(url)
  143.         # agent not found ==> access granted
  144.         return True
  145.  
  146.  
  147.     def __str__(self):
  148.         return ''.join([str(entry) + "\n" for entry in self.entries])
  149.  
  150.  
  151. class RuleLine:
  152.     """A rule line is a single "Allow:" (allowance==True) or "Disallow:"
  153.        (allowance==False) followed by a path."""
  154.     def __init__(self, path, allowance):
  155.         if path == '' and not allowance:
  156.             # an empty value means allow all
  157.             allowance = True
  158.         self.path = urllib.quote(path)
  159.         self.allowance = allowance
  160.  
  161.     def applies_to(self, filename):
  162.         return self.path == "*" or filename.startswith(self.path)
  163.  
  164.     def __str__(self):
  165.         return (self.allowance and "Allow" or "Disallow") + ": " + self.path
  166.  
  167.  
  168. class Entry:
  169.     """An entry has one or more user-agents and zero or more rulelines"""
  170.     def __init__(self):
  171.         self.useragents = []
  172.         self.rulelines = []
  173.  
  174.     def __str__(self):
  175.         ret = []
  176.         for agent in self.useragents:
  177.             ret.extend(["User-agent: ", agent, "\n"])
  178.         for line in self.rulelines:
  179.             ret.extend([str(line), "\n"])
  180.         return ''.join(ret)
  181.  
  182.     def applies_to(self, useragent):
  183.         """check if this entry applies to the specified agent"""
  184.         # split the name token and make it lower case
  185.         useragent = useragent.split("/")[0].lower()
  186.         for agent in self.useragents:
  187.             if agent == '*':
  188.                 # we have the catch-all agent
  189.                 return True
  190.             agent = agent.lower()
  191.             if agent in useragent:
  192.                 return True
  193.         return False
  194.  
  195.     def allowance(self, filename):
  196.         """Preconditions:
  197.         - our agent applies to this entry
  198.         - filename is URL decoded"""
  199.         for line in self.rulelines:
  200.             if line.applies_to(filename):
  201.                 return line.allowance
  202.         return True
  203.  
  204. class URLopener(urllib.FancyURLopener):
  205.     def __init__(self, *args):
  206.         urllib.FancyURLopener.__init__(self, *args)
  207.         self.errcode = 200
  208.  
  209.     def prompt_user_passwd(self, host, realm):
  210.         ## If robots.txt file is accessible only with a password,
  211.         ## we act as if the file wasn't there.
  212.         return None, None
  213.  
  214.     def http_error_default(self, url, fp, errcode, errmsg, headers):
  215.         self.errcode = errcode
  216.         return urllib.FancyURLopener.http_error_default(self, url, fp, errcode,
  217.                                                         errmsg, headers)
  218.